前端驗證和後端驗證他們的一些主要目的有些不同:
安全性:
前端程式碼(例如JavaScript)可以被修改或禁用。惡意的用戶可以很容易地繞過前端驗證。因此,後端驗證是確保數據安全和完整性的關鍵步驟。
開啟開發者工具:
在Chrome瀏覽器中,你可以右鍵點擊頁面元素並選擇「檢查」,
在「Elements」或「Sources」面板中,你可以直接修改HTML和JavaScript。
禁用JavaScript:
用戶可以透過瀏覽器設定來完全禁用JavaScript。
例如:在Chrome中,前往「設定」 > 「隱私權和安全性」 > 「網站設定」,然後你可以找到「JavaScript」選項,
資料完整性:
後端驗證可以確保存儲在數據庫中的數據是正確和一致的。也就是盡量保持輸入進來的參數和最後資料庫顯示出來的資訊是一樣的。
可靠性:網路連接可能會出問題,或者前端代碼可能有bug,這可能會導致不正確的數據被提交。後端驗證作為一個最後的防護,確保這些數據不會進入系統。
關於表單驗證有些比較簡單的我都是直接寫在controller,比如:
public function store(Request $request)
{
$validationData = $request->validate(
[
'name' => 'required|max:255|string|unique:natures,name',
]
Nature::create([
'name' => $validationData['name']
]);
return response(['message' => 'Nature saved successfully'], 201);
}
但這樣的寫法在驗證規則很多的時候,controller會顯得很大包。
像我另一個驗證邏輯比較多的就另外創一個request的class然後寫在裡面:
創建指令:
php artisan make:request PokemonRequest
<?php
namespace App\Http\Requests;
use App\Models\Pokemon;
use App\Models\Race;
use App\Rules\SkillJudgment;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
class StorePokemonRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => 'required|string|max:15|unique:pokemons,name',
'race_id' => 'required|integer|exists:races,id',
'ability_id' => 'required|integer|exists:abilities,id',
'nature_id' => 'required|integer|exists:natures,id',
'level' => 'required|integer|min:1|max:100',
'skills' => 'required|array|min:1|max:4',
'skills.*' => 'integer|exists:skills,id'
];
}
一般的寫法大概像這樣,裡面會用laravel預設好的一些驗證邏輯,只是說像我的寶可夢表單有把技能存成陣列我除了要驗證這些技能是否存在於技能表,還必須驗證我這隻寶可夢是否可以學這個技能。
自定義驗證邏輯
在上述的rule()return 後,laravel底層會去將這些驗證邏輯創建一個$validator物件,而這個物件他有一個after方法,就是在上述預設的一些規則外還可以在加一些使用者自行去定義的規則,
public function withValidator($validator)
{
$validator->after(function ($validator) {
// 如果已有錯誤,則直接返回不做後續驗證
if ($validator->failed() || is_null($this->skills) || !is_array($this->skills)) {
return true; // 如果沒有提供技能,則直接返回,不進行後續操作。
}
// 在這裡做了一個skills的額外驗證,確認輸入的skill是否是該種族可以學的
$raceId = $this->input('race_id'); // 假设 race_id 是在请求中的一个字段
$race = Race::find($raceId);
if (!validSkillsForRace($this->skills, $race)) {
$validator->errors()->add('skills', 'The skill is not allowed for this race.');
}
});
}
這裡我在if判斷是中有先去擋掉一些東西,比如說當使用者輸入不存在的種族id我後續會噴錯誤。
因為我pokemon store及update的controller都有用到需要確認寶可夢是否可以學習這個技能的需求,所以我把它寫成了輔助函數。
<?php
function validSkillsForRace($skills, $race) {
$allowedSkills = $race->skills->pluck('id')->toArray();
foreach ($skills as $skillId) {
if (!in_array($skillId, $allowedSkills)) {
return false;
}
}
return true;
}
這裡其實就是去我技能表撈資料轉成陣列,再用使用者輸入的陣列去和撈出來的所有技能的陣列做比較,如果不存在於技能表陣列就回傳自定義的錯誤訊息。
目前功能是有做出來,但後續的coding style以及例外的錯誤處理,應該還有很多需要改進的地方。